﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using MCB.Trading.Generic;

namespace MCB.Trading
{
    public class MatchingEngine<TOrderKey, TOrder, TProduct> 
        where TOrder : IOrder<TOrderKey, TProduct>
    {
        protected Dictionary<TProduct, OrderBook<TOrderKey, TOrder, TProduct>> _orderBooks  
            = new Dictionary<TProduct,OrderBook<TOrderKey,TOrder,TProduct>>();

        /// <summary>
        /// Order to product mappings
        /// </summary>
        protected ConcurrentDictionary<TOrderKey, TProduct> _orderProductMappings = new ConcurrentDictionary<TOrderKey, TProduct>();

        protected IAlgorithm<TOrderKey, TOrder, TProduct> Algorithm { get; set; }

        public delegate void MatchedDelegate(object sender, TOrder item);

        public event MatchedDelegate Matched;

        public MatchingEngine()
        {
        }

        public void AddAlgorithm(IAlgorithm<TOrderKey,TOrder, TProduct> algorithm)
        {
            Algorithm = algorithm;
            Algorithm.Matched = HasMatched;
        }

        public void Add(TOrder item)
        {
            TryGetOrderBook(item.Product).Add(item);
            Match(item);
        }

        public void Remove(TOrderKey key)
        {
            TProduct product;
            if (_orderProductMappings.TryGetValue(key, out product))
                TryGetOrderBook(product).Remove(key);
        }

        protected void Match(TOrder item)
        {
            Algorithm.Match(TryGetOrderBook(item.Product), item);  
        }

        protected void HasMatched(OrderBook<TOrderKey, TOrder, TProduct> book, TOrder value)
        {
            if (Matched!=null)
                Matched(this, value);
        }

        protected OrderBook<TOrderKey, TOrder, TProduct> TryGetOrderBook(TProduct product)
        {
            OrderBook<TOrderKey, TOrder, TProduct> book;
            if (!_orderBooks.TryGetValue(product, out book))
            {
                book = new OrderBook<TOrderKey, TOrder, TProduct>();
                _orderBooks.Add(product, book);
            }
            return book;
        }
    }
}
